﻿/*
 * *******************************************************
 *
 * 作者：hzy
 *
 * 开源地址：https://gitee.com/hzy6
 *
 * *******************************************************
 */

namespace HZY.Framework.Repository.EntityFramework.Repositories.Impl;

/// <summary>
/// 
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TDbContext"></typeparam>
public abstract class RepositoryCoreImpl<T, TDbContext> : IRepositoryCore<T>
    where T : class, new()
    where TDbContext : DbContext
{
    /// <summary>
    /// 数据上下文
    /// </summary>
    protected readonly TDbContext Context;

    /// <summary>
    /// 主键的 PropertyInfo 对象
    /// </summary>
    protected readonly PropertyInfo? KeyPropertyInfo;

    /// <summary>
    /// 过滤条件
    /// </summary>
    protected Expression<Func<T, bool>>? Filter;

    /// <summary>
    /// 是否忽略过滤
    /// </summary>
    protected bool IsIgnoreQueryFilter;

    /// <summary>
    /// 基础仓储
    /// </summary>
    /// <param name="dbContext"></param>
    /// <param name="filter"></param>
    protected RepositoryCoreImpl(TDbContext dbContext, Expression<Func<T, bool>>? filter = null)
    {
        Context = dbContext;
        KeyPropertyInfo = typeof(T).GetKeyProperty(false);
        Filter = filter;
        IsIgnoreQueryFilter = false;
    }

    /// <summary>
    /// 设置 跟踪 Attach
    /// </summary>
    /// <param name="model"></param>
    /// <param name="entityState"></param>
    public virtual void SetEntityState(T model, EntityState entityState)
    {
        Context.Entry(model).State = entityState;
    }

    /// <summary>
    /// 取消了实体对象的追踪操作 需要调用此函数 才能进行对实体数据库操作
    /// <para>
    /// 用于取消旧实体追踪缓存 防止出现 id 重复问题
    /// </para>
    ///  
    /// <para>
    /// 此函数解决的问题可以看此案例： https://blog.51cto.com/u_15064638/4401901
    /// </para>
    /// 
    /// </summary>
    /// <param name="detachedWhere"></param>
    public virtual void DetachWhenExist(Func<T, bool> detachedWhere)
    {
        var local = UnitOfWork.DbSet<T>().Local.FirstOrDefault(detachedWhere);
        if (local == null) return;
        SetEntityState(local, EntityState.Detached);
    }

    /// <summary>
    /// 生成表达式树 例如：( w=>w.Key==Guid.Empty )
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public virtual Expression<Func<T, bool>> GetKeyEqualExpression(object? value)
    {
        if (!string.IsNullOrWhiteSpace(KeyPropertyInfo?.Name))
        {
            return EntityFrameworkUtil.Equal<T, object?>(KeyPropertyInfo.Name, value);
        }

        throw new Exception("未找到主键");
    }

    /// <summary>
    /// 生成表达式树 例如：( w=> keyList.Contains( w.Key ) )
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public virtual Expression<Func<T, bool>> GetKeyContainsExpression(IEnumerable<object>? value)
    {
        if (!string.IsNullOrWhiteSpace(KeyPropertyInfo?.Name))
        {
            return EntityFrameworkUtil.Contains<T, object?>(KeyPropertyInfo.Name, value);
        }

        throw new Exception("未找到主键");
    }

    /// <summary>
    /// 获取 dbContext 对象
    /// </summary>
    /// <typeparam name="TDbContextResult"></typeparam>
    /// <returns></returns>
    public virtual TDbContextResult? GetContext<TDbContextResult>() where TDbContextResult : DbContext
        => Context as TDbContextResult;

    /// <summary>
    /// 获取上下文基础对象 DbContext
    /// </summary>
    /// <returns></returns>
    public virtual DbContext? GetContext() => GetContext<DbContext>();

    /// <summary>
    /// 工作单元
    /// </summary>
    public virtual IUnitOfWork UnitOfWork
    {
        get
        {
            if (Context is IBaseDbContext unitOfWorkDbContext)
            {
                return unitOfWorkDbContext.UnitOfWork;
            }

            return new UnitOfWorkImpl<DbContext>(Context);
        }
    }
}